/*
 * Project CHEN Rendering Engine : /heads.hpp
 * Created: 2020-12-16, Last modified:  2021-02-22
 * 
 * This is the universal header file of the project 
 * including class forward declarations and very fundamental
 * and simple functions and templates & global constants as
 * well as compile flags.
 * 
 * Copyright (C) 2021 Hineven (hineven@pku.edu.cn)
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */



#ifndef _H_HEADS
#define _H_HEADS


// Use float as Float, the universal floating point class
// used in the project
#define USE_FLOAT_FLOAT
// Use simple RGB representation of any light in computing
#define RGB_SPECTRUM

#include <iostream>
#include <glog/logging.h>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <climits>
#include <cfloat>
#include <cmath>
#include <algorithm>
#include <vector>
#include <assert.h>
#include <functional>

#define unused(a) (void(a))

typedef long long Long;

#ifdef USE_FLOAT_FLOAT
#define Float float
#define Float_cwidth 23
#define Float_ewidth 8
#define Float_length 32
#define Infinity ((Float)(1.0/(+0.0)))
#endif

#ifdef USE_DOUBLE_FLOAT
#define Float double
#define Float_cwidth 52
#define Float_ewidth 11
#define Float_length 64
#define Infinity ((Float)(1.0/(+0.0)))
#endif

#define NCORES 8


namespace chen {
class Vector2;
class Vector3;
class Point2;
class Point3;
class Bound2;
class Bound3;
class Normal;
class Ray;
class Object;
class Interact;
class SurfInteract;
class Spectrum;
class Transform;
class Sphere;
class Light;
class AreaLight;
class Scene;
class Structure;
class Film;
class Camera;
class CameraTask;
class Sampler;
class Shader;
class BasicShader;
class Shape;
class Material;
class BSDF;
class BSSRDF;
template<class T> class Texture;

constexpr Float epsilon = 2e-4;

template<class T> T abs (const T & t) {return t < 0 ? -t : t;}
template<class T> T max (const T & a, const T & b) {return a < b ? b : a;}
template<class T> T min (const T & a, const T & b) {return a < b ? a : b;}
template<class T> T lerp (const T & a, const T & b, Float v) {return a*((Float)1.0-v)+b*v;}


inline bool eequal(Float a, Float b) {
    return abs(a-b) < epsilon;
}
inline bool requal(Float a, Float b) {
    if(eequal(a, b)) return true;
    if(a*b <= 0) return false;
    Float f1 = max(abs(a), abs(b));
    Float f2 = min(abs(a), abs(b));
    return 1-epsilon < f2/f1;
}

template<class T1, class T2> inline T1 zdiv (T1 a, T2 b) {
    if(b == 0) return 0;
    return a/b;
}

constexpr Float float_epsilon = std::numeric_limits<Float>::epsilon();

// used for floating point error calculations
constexpr Float gamma (int n) {
    return n*float_epsilon/(1-n*float_epsilon);
}

inline Float clamp (Float l, Float r, Float a) {
    if(a < l) return l;
    if(a > r) return r;
    return a;
}

// Gamma correct function parsed from pbrt.h
inline Float gammaCorrect (Float value) { 
    if (value <= 0.0031308f) return 12.92f * value;
    return 1.055f * std::pow(value, (Float)(1.f / 2.4f)) - 0.055f;
}

template< class T > inline int maxDimension (const T & a) {
    if(a[0] > a[1] && a[0] > a[2]) return 0;
    if(a[1] > a[2]) return 1;
    return 2;
}

template< class T > inline T permute (T a, int x, int y, int z) {
    Float t1 = a[x], t2 = a[y], t3 = a[z];
    a[0] = t1, a[1] = t2, a[2] = t3;
    return a;
}

template<int C, class T> inline T product (T* arr) {
    if(C == 0) return 1;
    else if(C == 1) return arr[0];
    else if(C == 2) return arr[0]*arr[1];
    else if(C == 3) return arr[0]*arr[1]*arr[2];
    else if(C == 4) return (arr[0]*arr[1])*(arr[2]*arr[3]);
    else if(C == 5) return arr[0]*(arr[1]*arr[2])*(arr[3]*arr[4]);
    else if(C == 6) return (arr[0]*arr[1])*(arr[2]*arr[3])*(arr[4]*arr[5]);
    else throw "product template function fucked."; 
}

// calculate the index of the highest bit of a number
// possibly faster using __builtin__ functions
inline int hiBit (unsigned a) {
    int r = 0;
    if(a&0xffff0000u) a >>= 16, r += 16;
    if(a&0xff00u) a >>= 8, r += 8;
    if(a&0xf0u) a >>= 4, r += 4;
    if(a&12) a >>= 2, r += 2;
    return ((a&2)>>1)+(a&1)+r;
}

inline bool isNan (Float f) {
    return !(f == f);
}

// Deprecated unsafe random function
inline Float _rand01 () {
    return (Float)rand()/(Float)RAND_MAX;
}

template<class T> inline T pw2 (T a) {return a*a;}

// Guaranteed that *x1 <= *x2
template<class T> inline bool quadratic (T a, T b, T c, T * x1, T * x2) {
    T de = b*b-a*c*(Float)4;
    if(de < (Float)0) return false;
    de = sqrt(de);
    *x1 = (-b-de)/(a*(Float)2);
    *x2 = (-b+de)/(a*(Float)2);
    return true;
}

union Fbits
{
    Float f;
#ifdef USE_FLOAT_FLOAT
    unsigned i;
#endif
#ifdef USE_DOUBLE_FLOAT
    unsigned Long i;
#endif
};



#ifdef USE_FLOAT_FLOAT
inline Float nextFloat (Float f) {
    if(f == Infinity) return Infinity;
    if(f == 0) return 0x1p-149f;
    unsigned * t = (unsigned*)((void*)&f);
    if((*t)&(0x80000000u)) (*t) --;
    else (*t) ++;
    return *((Float*)((void*)t));
}

inline Float prevFloat (Float f) {
    if(f == -Infinity) return -Infinity;
    if(f == 0) return -0x1p-149f;
    unsigned * t = (unsigned*)((void*)&f);
    if((*t)&(0x80000000u)) (*t) ++;
    else (*t) --;
    return *((Float*)((void*)t));
}
#endif 


#ifdef USE_DOUBLE_FLOAT
inline Float nextFloat (Float f) {
    if(f == Infinity) return Infinity;
    if(f == 0) return 0x1p-1074;
    unsigned long long * t = (unsigned long long*)((void*)&f);
    if((*t)&(0x8000000000000000ull)) (*t) --;
    else (*t) ++;
    return *((Float*)((void*)t));
}

inline Float prevFloat (Float f) {
    if(f == -Infinity) return -Infinity;
    if(f == 0) return -0x1p-1074;
    unsigned * t = (unsigned*)((void*)&f);
    if((*t)&(0x8000000000000000ull)) (*t) ++;
    else (*t) --;
    return *((Float*)((void*)t));
}
#endif 

#ifdef USE_FLOAT_FLOAT
constexpr Float float_one_minus = 0x0.fffffep0;
#endif

#ifdef USE_DOUBLE_FLOAT
constexpr Float float_one_minus = 0x0.fffffffffffffep0;
#endif

}
#endif

#include "flags.hpp"